#include <Arduino.h>
#include <string.h> // 需要包含头文件
#include <LiquidCrystal.h>//
#include "OneNETSDK.h"
#include <EEPROM.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <NTPClient.h> // 引入NTPClient库文件，用于NTP时间同步
#define PRODUCT_KEY "RkCQ13amCk"
#define DEVICE_SECRET "eWtzc5mpiQmt++GNR0FNMyO5Hak7zxPJ9UdZn9G7N5c="
const char *ntpServer = "pool.ntp.org";                     // 默认NTP服务器
const long gmtOffset_sec = 28800;                           // 北京时间 (UTC+8) 的时区偏移量
const int daylightOffset_sec = 0;                           // 不考虑夏令时
WiFiUDP udp;                                                // 用于NTP时间同步的UDP客户端对象
NTPClient timeClient(udp, ntpServer, gmtOffset_sec, 60000); // 每60秒更新一次
const byte ROWS = 4;
const byte COLS = 4;
#define EEPROM_SIZE 128
int ldnum = 0;
struct MyData
{
  int timesd = 10;
  int timenum = 5;
  char toptxt2[16] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
} retrievedData;
struct MyData1
{
  int timesd = 10;
  int timenum = 5;
  char toptxt2[16] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
} retrievedData1;

byte colPins[ROWS] = {25, 26, 27, 14};
byte rowPins[COLS] = {12, 13, 15, 2};

char keys[ROWS][COLS] = {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'}};

// 防抖变量
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
bool lastKeyState = HIGH;
bool keyPressed = false;
const int rs = 17, en = 18, d4 = 22, d5 = 23, d6 = 33, d7 = 32;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
TaskHandle_t CoreTask1; // 声明CoreTask1任务句柄，用于在核心1上运行任务
TaskHandle_t CoreTask2; // 声明CoreTask2任务句柄，用于在核心2上运行任务
TaskHandle_t CoreTask3; // 声明CoreTask3任务句柄，用于在核心2上运行任务
TaskHandle_t CoreTask4; // 声明CoreTask4任务句柄，用于在核心2上运行任务
TaskHandle_t CoreTask5; // 声明CoreTask5任务句柄，用于在核心2上运行任务
int alarmsuo = 0;
char key;
char key1;
char toptxt[16];
char toptxt1[16];
int toptxttemp = 0;
int suozt = 0;
char DEV_ChipID[16];
int unlocking = 0;
int zt = 0;
static WiFiClient espClient;
bool containsSubstring(const char *mainStr, const char *subStr)
{
  return strstr(mainStr, subStr) != nullptr;
}

void unlock(JsonVariant unlocking)
{
  int w1_on = unlocking["unlocking"];
  if (w1_on)
  {
    digitalWrite(4, HIGH);
    digitalWrite(16, HIGH);
    vTaskDelay(3000);
    digitalWrite(16, LOW);
    vTaskDelay(3000);
    digitalWrite(4, LOW);
    digitalWrite(16, LOW);
  }
  else
  {
    digitalWrite(4, LOW);
  }
}
void settime(JsonVariant lockingtime)
{
  retrievedData.timesd = lockingtime["lockingtime"];
  Serial.println(retrievedData.timesd);
  retrievedData1.timesd = retrievedData.timesd;
  EEPROM.put(0, retrievedData1);
  // 提交更改（必须调用commit才能使更改生效）
  if (EEPROM.commit())
  {
    Serial.println("Data written successfully");
  }
  else
  {
    Serial.println("Commit failed");
  }
  zt = 3;
  OneNETSDK::send("lockingtime", retrievedData.timesd);
  OneNETSDK::loop();
}
void setnum(JsonVariant Lockingfrequency)
{
  retrievedData.timenum = Lockingfrequency["Lockingfrequency"];
  Serial.println(retrievedData.timenum);
  retrievedData1.timenum = retrievedData.timenum;
  EEPROM.put(0, retrievedData1);

  // 提交更改（必须调用commit才能使更改生效）
  if (EEPROM.commit())
  {
    Serial.println("Data written successfully");
  }
  else
  {
    Serial.println("Commit failed");
  }
  zt = 2;
  OneNETSDK::send("Lockingfrequency", retrievedData.timenum);
  OneNETSDK::loop();
}
void setpw(JsonVariant Doorockpassword)
{
  memset(retrievedData.toptxt2, 0, sizeof(retrievedData.toptxt2)); // 完全清空不留痕迹
  strncpy(retrievedData.toptxt2, Doorockpassword["Doorockpassword"], strlen(Doorockpassword["Doorockpassword"]));
  Serial.println(retrievedData.toptxt2);
  strcpy(retrievedData1.toptxt2, retrievedData.toptxt2);
  EEPROM.put(0, retrievedData1);

  // 提交更改（必须调用commit才能使更改生效）
  if (EEPROM.commit())
  {
    Serial.println("Data written successfully");
  }
  else
  {
    Serial.println("Commit failed");
  }
  zt = 1;
  OneNETSDK::send("Doorockpassword", retrievedData.toptxt2);
  OneNETSDK::loop();
}
char scanKeys()
{
  for (byte c = 0; c < COLS; c++)
  {
    digitalWrite(colPins[c], LOW);
    for (byte r = 0; r < ROWS; r++)
    {
      if (digitalRead(rowPins[r]) == LOW)
      {
        digitalWrite(colPins[c], HIGH);
        return keys[r][c];
      }
    }
    digitalWrite(colPins[c], HIGH);
  }
  return '\0'; // 无按键按下
}
void laishuo(bool temp)
{
  switch (temp)
  {
  case 0:
    digitalWrite(16, HIGH);
    vTaskDelay(200);
    digitalWrite(16, LOW);
    vTaskDelay(200);
    digitalWrite(16, HIGH);
    vTaskDelay(200);
    digitalWrite(16, LOW);
    vTaskDelay(200);
    digitalWrite(16, HIGH);
    vTaskDelay(200);
    digitalWrite(16, LOW);
    vTaskDelay(200);
    digitalWrite(16, HIGH);
    vTaskDelay(200);
    digitalWrite(16, LOW);
    vTaskDelay(200);
    break;
  case 1:
    digitalWrite(4, HIGH);
    digitalWrite(16, HIGH);
    vTaskDelay(3000);
    digitalWrite(16, LOW);
    vTaskDelay(3000);
    digitalWrite(4, LOW);
    digitalWrite(16, LOW);
    break;
  case 2:
    digitalWrite(16, HIGH);
    vTaskDelay(500);
    digitalWrite(16, LOW);
    vTaskDelay(500);
    digitalWrite(16, HIGH);
    vTaskDelay(500);
    digitalWrite(16, LOW);
    vTaskDelay(500);
    break;

  default:
    break;
  }
}
void task1(void *pvParameters)
{
  Serial.begin(115200);
  for (byte r = 0; r < ROWS; r++)
  {
    pinMode(rowPins[r], INPUT_PULLUP);
  }
  for (byte c = 0; c < COLS; c++)
  {
    pinMode(colPins[c], OUTPUT);
    digitalWrite(colPins[c], HIGH);
  }

  Serial.println("ESP32 4x4按键矩阵已就绪（优化版）");
  while (1)
  {
    key = scanKeys();
    if (key != '\0')
    {
      // 检测到按键按下
      if (!keyPressed)
      {
        keyPressed = true;
        Serial.print("按键按下: ");
        Serial.println(key);
        key1 = key;
        toptxt[toptxttemp] = key1;
        toptxt1[toptxttemp] = '*';
        toptxttemp++;
      }
    }
    else
    {
      keyPressed = false;
    }
    vTaskDelay(10);
  }
}
void task2(void *pvParameters)
{
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);
  // 初始化LCD，设置列数和行数 (16列2行)
  lcd.begin(16, 2);

  while (1)
  {
    lcd.setCursor(0, 0);
    lcd.print("welcome home!!!!!!!");
    bool a = containsSubstring(toptxt, retrievedData.toptxt2);
    lcd.setCursor(0, 1);
    lcd.print(toptxt1);
    lcd.setCursor(0, 1);
    if (alarmsuo >= retrievedData.timenum)
    {
      memset(toptxt, 0, sizeof(toptxt)); // 完全清空不留痕迹
      lcd.setCursor(0, 0);
      lcd.print("***************************");
      lcd.setCursor(0, 1);
      lcd.print("***************************");
      laishuo(0);
      vTaskDelay(retrievedData.timesd * 1000);
      for (int i = 0; i < toptxttemp; i++)
      {
        toptxt1[i] = ' ';
      }
      toptxttemp = 0;
      alarmsuo = 0;
      suozt = 2;
    }
    else
    {
      if (toptxttemp >= 16)
      {
        memset(toptxt, 0, sizeof(toptxt)); // 完全清空不留痕迹
        lcd.setCursor(0, 1);
        lcd.print("The password long");
        laishuo(0);
        lcd.setCursor(0, 1);
        vTaskDelay(1000);
        for (int i = 0; i < toptxttemp; i++)
        {
          toptxt1[i] = ' ';
        }
        toptxttemp = 0;
        alarmsuo++;
      }
    }

    if (a)
    {
      memset(toptxt, 0, sizeof(toptxt)); // 完全清空不留痕迹
      lcd.setCursor(0, 1);
      lcd.print("The password correct.");
      laishuo(1);
      lcd.setCursor(0, 1);
      lcd.setCursor(0, 1);
      lcd.print("                      ");
      lcd.setCursor(0, 1);
      vTaskDelay(1000);
      for (int i = 0; i < toptxttemp; i++)
      {
        toptxt1[i] = ' ';
      }
      alarmsuo = 0;
      toptxttemp = 0;
      suozt = 1;
    }
    switch (zt)
    {
    case 1:
      lcd.setCursor(0, 1);
      lcd.print("password been changed.");
      vTaskDelay(1000);
      lcd.setCursor(0, 1);
      lcd.print("                      ");
      break;
    case 2:
      lcd.setCursor(0, 1);
      lcd.print("number been changed.");
      vTaskDelay(1000);
      lcd.setCursor(0, 1);
      lcd.print("                      ");
      break;
    case 3:
      lcd.setCursor(0, 1);
      lcd.print("time been changed ");
      vTaskDelay(1000);
      lcd.setCursor(0, 1);
      lcd.print("                      ");
      break;
    case 4:
      lcd.setCursor(0, 1);
      lcd.print("Someone's outside ");
      vTaskDelay(1000);
      lcd.setCursor(0, 1);
      lcd.print("                      ");
      break;
    default:
      break;
    }
    zt = 0;
    // 每隔2秒更新一次
    vTaskDelay(20);
  }
}
void task3(void *pvParameters)
{
  char Doorlockdynamics[256];
  timeClient.begin(); // 初始化timeClient对象
  while (1)
  {
    timeClient.update();                                 // 获取并更新时间
    unsigned long epochTime = timeClient.getEpochTime(); // 获取时间戳
    struct tm *ptm = gmtime((time_t *)&epochTime);       // 将时间戳转换为tm结构体
    int year = ptm->tm_year + 1900;                      // 获取年份
    int month = ptm->tm_mon + 1;                         // 获取月份，月份从0开始，所以加1 // 月份从 0 开始
    int day = ptm->tm_mday;                              // 获取日期
    int hour = timeClient.getHours();                    // 获取小时
    int minute = timeClient.getMinutes();                // 获取分钟
    int second = timeClient.getSeconds();                // 获取秒
    switch (suozt)
    {
    case 1:
      snprintf(Doorlockdynamics, 256, "%d-%d-%d-%d-%d-%dPassword correct. Welcome home.", year, month, day, hour, minute, second);
      OneNETSDK::send("Doorlockdynamics", Doorlockdynamics);
      ldnum = 0;
      break;
    case 2:
      snprintf(Doorlockdynamics, 256, "%d-%d-%d-%d-%d-%dWarning: The door lock is locked.", year, month, day, hour, minute, second);
      OneNETSDK::send("Doorlockdynamics", Doorlockdynamics);
      break;
    case 3:
      if (ldnum >= 200)
      {
        snprintf(Doorlockdynamics, 256, "%d-%d-%d-%d-%d-%dSomeone's outside.", year, month, day, hour, minute, second);
        OneNETSDK::send("Doorlockdynamics", Doorlockdynamics);
        ldnum = 0;
      }
      break;
    default:
      break;
    }
    OneNETSDK::loop();
    suozt = 0;
    vTaskDelay(20);
  }
}
void task4(void *pvParameters)
{
  Serial.begin(115200);
  pinMode(36, INPUT);
  while (1)
  {
    if (digitalRead(36) == LOW)
    {
      ldnum += 5;
      Serial.println(ldnum);
      if (ldnum >= 200)
      {
        suozt = 3;
        zt = 4;
      }
      Serial.println(suozt);
      vTaskDelay(100);
    }
  }
}
void Loop_Fun()
{

  if (WiFi.status() != WL_CONNECTED)
  { // 尝试重新联网
    WiFi.reconnect();
    for (int j = 0; j < 15; j++)
    {
      if (WiFi.status() == WL_CONNECTED)
      {
        break;
      }
      else
      {
        vTaskDelay(1000);
      }
    }
  }
}
void setup()
{
  pinMode(4, OUTPUT);
  pinMode(16, OUTPUT);
  digitalWrite(4, LOW);
  digitalWrite(16, LOW);
  Serial.begin(115200);
  // 初始化EEPROM
  if (!EEPROM.begin(EEPROM_SIZE))
  {
    Serial.println("Failed to initialize EEPROM");
    while (1)
      vTaskDelay(1000); // 无限等待
  }
  EEPROM.get(0, retrievedData1);
  // 打印读取的数据
  Serial.println("\nRetrieved data:");
  Serial.print("Value1: ");
  Serial.println(retrievedData1.timesd);
  Serial.print("Value2: ");
  Serial.println(retrievedData1.timenum);
  Serial.print("Text: ");
  Serial.println(retrievedData1.toptxt2);
  Serial.print("Text length: ");
  Serial.println(strlen(retrievedData1.toptxt2));
  retrievedData.timesd = retrievedData1.timesd;
  retrievedData.timenum = retrievedData1.timenum;
  strcpy(retrievedData.toptxt2, retrievedData1.toptxt2);
  WiFi.begin("styn-iot", "st7713926813");
  uint32_t chipId = 0;
  for (int i = 0; i < 17; i = i + 8)
  {
    chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
  }
  snprintf(DEV_ChipID, 16, "ELE-%06X", chipId);

  OneNETSDK::begin(espClient, PRODUCT_KEY, DEV_ChipID, DEVICE_SECRET);
  vTaskDelay(5000);
  OneNETSDK::bindData("unlocking", unlock);
  OneNETSDK::bindData("lockingtime", settime);
  OneNETSDK::bindData("Lockingfrequency", setnum);
  OneNETSDK::bindData("Doorockpassword", setpw);
  OneNETSDK::send("lockingtime", retrievedData.timesd);
  OneNETSDK::send("Lockingfrequency", retrievedData.timenum);
  OneNETSDK::send("Doorockpassword", retrievedData.toptxt2);
  OneNETSDK::send("WifiName", (char *)WiFi.SSID().c_str());
  OneNETSDK::send("RSSI", WiFi.RSSI());
  xTaskCreatePinnedToCore(
      task1,      // 实现任务的函数名称
      "HQQX",     // 任务的任何名称，按实际功能起名
      16384,      // 分配给任务的堆栈大小，以字节为单位，先尝试一下分配16K够不够
      NULL,       // 任务输入参数，可以为NULL
      1,          // 任务优先级，3最高，0最低
      &CoreTask1, // 任务句柄，头部已定义，亦可以为NULL
      0);         // 任务运行的内核ID（0或者1）
                  // send
  xTaskCreatePinnedToCore(
      task2,      // 实现任务的函数名称
      "SEND",     // 任务的任何名称，按实际功能起名
      16384,      // 分配给任务的堆栈大小，以字节为单位，先尝试一下分配64K够不够
      NULL,       // 任务输入参数，可以为NULL
      1,          // 任务优先级，3最高，0最低
      &CoreTask2, // 任务句柄，头部已定义，亦可以为NULL
      1);         // 任务运行的内核ID（0或者1）
  //                 // 北斗
  xTaskCreatePinnedToCore(
      task3,      // 实现任务的函数名称
      "BDHQ",     // 任务的任何名称，按实际功能起名
      16384,      // 分配给任务的堆栈大小，以字节为单位，先尝试一下分配16K够不够
      NULL,       // 任务输入参数，可以为NULL
      0,          // 任务优先级，3最高，0最低
      &CoreTask3, // 任务句柄，头部已定义，亦可以为NULL
      0);         // 任务运行的内核ID（0或者1）
  xTaskCreatePinnedToCore(
      task4,      // 实现任务的函数名称
      "BDHQ",     // 任务的任何名称，按实际功能起名
      16384,      // 分配给任务的堆栈大小，以字节为单位，先尝试一下分配16K够不够
      NULL,       // 任务输入参数，可以为NULL
      0,          // 任务优先级，3最高，0最低
      &CoreTask4, // 任务句柄，头部已定义，亦可以为NULL
      0);         // 任务运行的内核ID（0或者1）
  OneNETSDK::loop();
}

void loop()
{
  Loop_Fun();
  OneNETSDK::loop();
  vTaskDelay(10);
}